用 Linux epoll 实现高性能 HTTP 服务器

您所在的位置:网站首页 linux epoll实现服务器 用 Linux epoll 实现高性能 HTTP 服务器

用 Linux epoll 实现高性能 HTTP 服务器

2023-08-01 19:13| 来源: 网络整理| 查看: 265

用 Linux epoll 实现高性能 HTTP 服务器

为了代码的整洁性,本文章所介绍功能将使用 C++ 实现。实际使用中可转为 C 语言使用。 此项目只能在Linux下使用,windows请绕道。

项目概括

本项目是使用 Linux epoll 实现的一个简单的 HTTP 服务器。仅支持 HTTP 1.0、GET 和 HEAD 方法,对 HTTP 请求报文仅使用正则表达式进行解析。在实际使用中,请使用词法和语法分析来实现请求报文的内容解析。

什么是 epoll

epoll 是 Linux 内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

准备编译环境

本程序需要 gcc,make 相关(make 不是必须)。可通过以下命令安装:

$ sudo apt-get install gcc $ sudo apt-get install make 项目代码

项目配套代码已上传到 GitHub,项目地址 https://github.com/ZiFung/epoll-http-server/

将项目源码下载下来后,用 terminal 打开,输入

$ make

后输入

$ ./my_http [port]

即可运行

项目成果

本项目经过 GitHub 上的开源项目 wrk 进行压力测试,在测试虚拟机上测得11000并发。

完整代码 项目结构 epoll_http/ html/ index.html main.cppHttpServer.hppHttpServer.cppHttpResponse.hppHttpResponse.cpp 代码 // main.cpp #include "HttpServer.hpp" #include #include #include using namespace std; int main(int argc, char *argv[]) { // 端口处理 unsigned short port = 80; if (argc > 3) { cerr cerr public: HttpServer(); ~HttpServer(); bool init_server(unsigned short port); void start_serving(); private: void add_event(int fd, int state); void modify_event(int fd, int state); void delete_event(int fd); private: int listen_fd = -1; int epfd = -1; struct epoll_event events[EPOLLEVENTS_SIZE]; HttpResponse client_data[MAX_SOCK_SIZE]; }; #endif // HTTPSERVER_HPP // HttpServer.cpp #include "HttpServer.hpp" #include #include #include #include #include #include #include using namespace std; HttpServer::HttpServer() { } HttpServer::~HttpServer() { if (listen_fd > 0) { close(listen_fd); listen_fd = -1; } } bool HttpServer::init_server(unsigned short port) { listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == listen_fd) { cerr cerr int ret = epoll_wait(epfd, events, EPOLLEVENTS_SIZE, -1); for (int i = 0; i sockaddr_in caddr; socklen_t len = sizeof(caddr); int client = accept(listen_fd, (sockaddr*)&caddr, &len); if (client int client = events[i].data.fd; if (events[i].events & EPOLLIN) { bool re = false; if (!re) { char buf[BUFFER_SIZE] = { 0 }; int revclen = recv(client, buf, BUFFER_SIZE, 0); if (revclen close(client); delete_event(client); client_data[client].request = ""; continue; } modify_event(client, EPOLLOUT); } else if (events[i].events & EPOLLOUT) { char buf[BUFFER_SIZE] = { 0 }; int data_read = client_data[client].read_data(buf, BUFFER_SIZE); int data_sent = send(client, buf, data_read, MSG_NOSIGNAL); if (data_sent == 0) continue; if (data_sent delete_event(client); client_data[client].close(); close(client); } } } } } close(listen_fd); } void HttpServer::add_event(int fd, int state) { struct epoll_event ev; ev.data.fd = fd; ev.events = state; epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); } void HttpServer::modify_event(int fd, int state) { struct epoll_event ev; ev.data.fd = fd; ev.events = state; epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev); } void HttpServer::delete_event(int fd) { epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); } // HttpSponse.hpp #ifndef HTTPRESPONSE_HPP #define HTTPRESPONSE_HPP #include #include using namespace std; class HttpResponse { public: HttpResponse(); ~HttpResponse(); string request = ""; bool parse_request(); int read_data(char *buf, int buf_size); void forward(int offset); bool is_reading_finished(); void close(); private: ifstream *in_file = NULL; string buffer = ""; }; #endif // HTTPRESPONSE_HPP // HttpResponse.cpp #include "HttpResponse.hpp" #include #include using namespace std; HttpResponse::HttpResponse() { } HttpResponse::~HttpResponse() { } bool HttpResponse::parse_request() { string type = "GET"; string path = "/"; if (request[3] == ' ') { int i; for (i = 4; request[i] != '?' && request[i] != ' '; i++); path = request.substr(4, i - 4); } else type = "HEAD"; buffer += "HTTP/1.1 200 OK\r\n"; unsigned long file_size = 0; if (type == "GET") { string filename = path; if(path == "/") filename = "/index.html"; string filepath = "html"; filepath += filename; in_file = new ifstream(filepath, ios::in | ios::binary); if(!in_file->is_open()) { in_file = new ifstream("html/404.html", ios::in | ios::binary); if(!in_file->is_open()) { cerr strcpy(buf, buffer.c_str()); if (in_file == NULL) return (int)buffer.size(); in_file->read(buf + buffer.size(), buf_size - (int)buffer.size()); int data_read = in_file->gcount(); in_file->seekg(-1 * data_read, ios::cur); return (int)buffer.size() + data_read; } else if (buffer.size() > 0) { string temp = buffer.substr(0, buf_size); strcpy(buf, temp.c_str()); return buf_size; } else { if (in_file == NULL) return -1; in_file->read(buf, buf_size); int data_read = in_file->gcount(); in_file->seekg(-1 * data_read, ios::cur); return data_read; } } void HttpResponse::forward(int offset) { if (buffer.size() > 0 && offset > (int)buffer.size()) { offset -= (int)buffer.size(); buffer = ""; in_file->seekg(offset, ios::cur); } else if (buffer.size() > 0) buffer = buffer.substr(offset - 1, buffer.size() - offset); else in_file->seekg(offset, ios::cur); } bool HttpResponse::is_reading_finished() { if (in_file != NULL) return in_file->peek() == EOF; return buffer.size() == 0; } void HttpResponse::close() { if (in_file != NULL) in_file->close(); in_file = NULL; buffer = ""; }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3